<template>
	<div class="app-container">
		<el-button type="primary" @click="handleAddRole">New Role</el-button>

		<el-table :data="rolesList" style="width: 100%;margin-top:30px;" border>
			<el-table-column align="center" label="Role Key" width="220">
				<template slot-scope="scope">
					{{ scope.row.key }}
				</template>
			</el-table-column>
			<el-table-column align="center" label="Role Name" width="220">
				<template slot-scope="scope">
					{{ scope.row.name }}
				</template>
			</el-table-column>
			<el-table-column align="header-center" label="Description">
				<template slot-scope="scope">
					{{ scope.row.description }}
				</template>
			</el-table-column>
			<el-table-column align="center" label="Operations">
				<template slot-scope="scope">
					<el-button type="primary" size="small" @click="handleEdit(scope)">Edit</el-button>
					<el-button type="danger" size="small" @click="handleDelete(scope)">Delete</el-button>
				</template>
			</el-table-column>
		</el-table>

		<el-dialog :visible.sync="dialogVisible" :title="dialogType==='edit'?'Edit Role':'New Role'">
			<el-form :model="role" label-width="80px" label-position="left">
				<el-form-item label="Name">
					<el-input v-model="role.name" placeholder="Role Name"></el-input>
				</el-form-item>
				<el-form-item label="Desc">
					<el-input v-model="role.description"
							  :autosize="{ minRows: 2, maxRows: 4}"
							  type="textarea"
							  placeholder="Role Description"></el-input>
				</el-form-item>
				<el-form-item label="Menus">
					<el-tree ref="tree"
							 :check-strictly="checkStrictly"
							 :data="routesData"
							 :props="defaultProps"
							 show-checkbox
							 node-key="path"
							 class="permission-tree"></el-tree>
				</el-form-item>
			</el-form>
			<div style="text-align:right;">
				<el-button type="danger" @click="dialogVisible=false">Cancel</el-button>
				<el-button type="primary" @click="confirmRole">Confirm</el-button>
			</div>
		</el-dialog>
	</div>
</template>

<script>
	import * as UtilsObj from "/vue/utils/index.js";
	const deepClone = UtilsObj.deepClone;
    import { getRoutes, getRoles, addRole, deleteRole, updateRole} from '/vue/api/role.js';
	const defaultRole = {
		key: '',
		name: '',
		description: '',
		routes: []
	}

	module.exports = {
		data() {
			return {
				role: Object.assign({}, defaultRole),
				routes: [],
				rolesList: [],
				dialogVisible: false,
				dialogType: 'new',
				checkStrictly: false,
				defaultProps: {
					children: 'children',
					label: 'title'
				}
			}
		},
		computed: {
			routesData() {
				return this.routes
			}
		},
		created() {
				// Mock: get all routes and roles list from server
				this.getRoutes();
				this.getRoles();
		},
		methods: {
			async getRoutes() {
				const res = await getRoutes()
				this.serviceRoutes = res.data
				this.routes = this.generateRoutes(res.data)
			},
			async getRoles() {
				const res = await getRoles()
				this.rolesList = res.data
			},

			// Reshape the routes structure so that it looks the same as the sidebar
			generateRoutes(routes, basePath = '/') {
				const res = []

				for (let route of routes) {
					// skip some route
					if (route.hidden) { continue }

					const onlyOneShowingChild = this.onlyOneShowingChild(route.children, route)

					if (route.children && onlyOneShowingChild && !route.alwaysShow) {
						route = onlyOneShowingChild
					}

					const data = {
                        path: UtilsObj.resolvePath(basePath, route.path),
						title: route.meta && route.meta.title

					}

					// recursive child routes
					if (route.children) {
						data.children = this.generateRoutes(route.children, data.path)
					}
					res.push(data)
				}
				return res
			},
			generateArr(routes) {
				let data = []
				routes.forEach(route => {
					data.push(route)
					if (route.children) {
						const temp = this.generateArr(route.children)
						if (temp.length > 0) {
							data = [...data, ...temp]
						}
					}
				})
				return data
			},
			handleAddRole() {
				this.role = Object.assign({}, defaultRole)
				if (this.$refs.tree) {
					this.$refs.tree.setCheckedNodes([])
				}
				this.dialogType = 'new'
				this.dialogVisible = true
			},
			handleEdit(scope) {
				this.dialogType = 'edit'
				this.dialogVisible = true
				this.checkStrictly = true
				this.role = deepClone(scope.row)
				this.$nextTick(() => {
					const routes = this.generateRoutes(this.role.routes)
					this.$refs.tree.setCheckedNodes(this.generateArr(routes))
					// set checked state of a node not affects its father and child nodes
					this.checkStrictly = false
				})
			},
			handleDelete({ $index, row }) {
				this.$confirm('Confirm to remove the role?', 'Warning', {
					confirmButtonText: 'Confirm',
					cancelButtonText: 'Cancel',
					type: 'warning'
				})
					.then(async () => {
						await deleteRole(row.key)
						this.rolesList.splice($index, 1)
						this.$message({
							type: 'success',
							message: 'Delete succed!'
						})
					})
					.catch(err => { console.error(err) })
			},
			generateTree(routes, basePath = '/', checkedKeys) {
				const res = []

				for (const route of routes) {
					const routePath = UtilsObj.resolvePath(basePath, route.path)

					// recursive child routes
					if (route.children) {
						route.children = this.generateTree(route.children, routePath, checkedKeys)
					}

					if (checkedKeys.includes(routePath) || (route.children && route.children.length >= 1)) {
						res.push(route)
					}
				}
				return res
			},
			async confirmRole() {
				const isEdit = this.dialogType === 'edit'

				const checkedKeys = this.$refs.tree.getCheckedKeys()
				this.role.routes = this.generateTree(deepClone(this.serviceRoutes), '/', checkedKeys)

				if (isEdit) {
					await updateRole(this.role.key, this.role)
					for (let index = 0; index < this.rolesList.length; index++) {
						if (this.rolesList[index].key === this.role.key) {
							this.rolesList.splice(index, 1, Object.assign({}, this.role))
							break
						}
					}
				} else {
					const { data } = await addRole(this.role)
					this.role.key = data.key
					this.rolesList.push(this.role)
				}

				const { description, key, name } = this.role
				this.dialogVisible = false
				this.$notify({
					title: 'Success',
					dangerouslyUseHTMLString: true,
					message: `
			<div>Role Key: ${key}</div>
			<div>Role Name: ${name}</div>
			<div>Description: ${description}</div>
		  `,
					type: 'success'
				})
			},
			// reference: src/view/layout/components/Sidebar/SidebarItem.vue
			onlyOneShowingChild(children = [], parent) {
				let onlyOneChild = null
				const showingChildren = children.filter(item => !item.hidden)

				// When there is only one child route, the child route is displayed by default
				if (showingChildren.length === 1) {
					onlyOneChild = showingChildren[0]
					onlyOneChild.path = UtilsObj.resolvePath(parent.path, onlyOneChild.path)
					return onlyOneChild
				}

				// Show parent if there are no child route to display
				if (showingChildren.length === 0) {
					onlyOneChild = { ...parent, path: '', noShowingChildren: true }
					return onlyOneChild
				}

				return false
			}
		}
	}
</script>

<style scoped>
	.app-container .roles-table {
		margin-top: 30px;
	}

	.app-container .permission-tree {
		margin-bottom: 30px;
	}
</style>
